Commit 6aaf8ab7 authored by Christian Ledermann's avatar Christian Ledermann

add styles, atom and (empty) gx

parent 4bb70a19
# -*- coding: utf-8 -*-
"""
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
search results, both in Earth browsers such as Google Earth, and in other
applications such as Google Maps. The ascription elements used in KML
are as follows:
atom:author element - parent element for atom:name
atom:name element - the name of the author
atom:link element - contains the href attribute
href attribute - URL of the web page containing the KML/KMZ file
These elements are defined in the Atom Syndication Format. The complete
specification is found at http://atompub.org.
"""
import logging
logger = logging.getLogger('fastkml.atom')
try:
from lxml import etree
LXML = True
except ImportError:
import xml.etree.ElementTree as etree
LXML = False
class Link(object):
"""
Identifies a related Web page. The type of relation is defined by
the rel attribute. A feed is limited to one alternate per type and
hreflang.
<link> is patterned after html's link element. It has one required
attribute, href, and five optional attributes: rel, type, hreflang,
title, and length.
"""
__name__ = 'Link'
ns = None
href = None
# href is the URI of the referenced resource
rel = None
# rel contains a single link relationship type.
# It can be a full URI, or one of the following predefined values
# (default=alternate):
# alternate: an alternate representation
# enclosure: a related resource which is potentially large in size
# and might require special handling, for example an audio or video
# recording.
# related: an document related to the entry or feed.
# self: the feed itself.
# via: the source of the information provided in the entry.
type = None
# indicates the media type of the resource
hreflang = None
# indicates the language of the referenced resource
title = None
# human readable information about the link
lenght = None
# the length of the resource, in bytes
def __init__(self, ns=None, href=None, rel=None, type=None,
hreflang=None, title=None, lenght=None):
if ns == None:
self.ns = '{http://www.w3.org/2005/Atom}'
else:
self.ns = ns
self.href = href
self.rel = rel
self.type = type
self.hreflang = hreflang
self.title = title
self.lenght = lenght
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
else:
if element.get('href'):
self.href = element.get('href')
else:
logger.critical('required attribute href missing')
raise TypeError
if element.get('rel'):
self.rel = element.get('rel')
if element.get('type'):
self.type = element.get('type')
if element.get('hreflang'):
self.hreflang = element.get('hreflang')
if element.get('title'):
self.title = element.get('title')
if element.get('lenght'):
self.rel = element.get('lenght')
return element
def etree_element(self):
element = etree.Element(self.ns + self.__name__.lower())
if self.href:
element.set('href', self.href)
else:
logger.critical('required attribute href missing')
raise TypeError
element.set('rel', self.rel)
element.set('type', self.type)
element.set('hreflang', self.hreflang)
element.set('title', self.title)
element.set('lenght', self.lenght)
class _Person(object):
"""
<author> and <contributor> describe a person, corporation, or similar
entity. It has one required element, name, and two optional elements:
uri, email.
"""
ns = None
name = None
#conveys a human-readable name for the person.
uri = None
#contains a home page for the person.
email = None
#contains an email address for the person.
def __init__(self, ns=None, name=None, uri=None, email=None):
if ns == None:
self.ns = '{http://www.w3.org/2005/Atom}'
else:
self.ns = ns
self.name = name
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.text = self.name
else:
logger.critical('No Name for person defined')
raise TypeError
if self.uri:
#XXX validate uri
uri = etree.SubElement(element, "%suri" %self.ns)
uri.text = self.uri
if self.email:
#XXX validate email
email = etree.SubElement(element, "%semail" %self.ns)
email.text = self.email
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
else:
name = element.find('%sname' %self.ns)
if name is not None:
self.name = name.text
uri = element.find('%suri' %self.ns)
if uri is not None:
self.uri = uri.text
email = element.find('%semail' %self.ns)
if email is not None:
self.email = email.text
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 -*-
"""
With the launch of Google Earth 5.0, Google has provided extensions to KML
to support a number of new features. These extensions use the gx prefix
and the following namespace URI:
xmlns:gx="http://www.google.com/kml/ext/2.2"
This namespace URI must be added to the <kml> element in any KML file
using gx-prefixed elements:
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2">
Extensions to KML may not be supported in all geo-browsers. If your
browser doesn't support particular extensions, the data in those
extensions should be silently ignored, and the rest of the KML file
should load without errors.
Elements that currently use the gx prefix are:
gx:altitudeMode
gx:altitudeOffset
gx:angles
gx:AnimatedUpdate
gx:balloonVisibility
gx:coord
gx:delayedStart
gx:drawOrder
gx:duration
gx:FlyTo
gx:flyToMode
gx:h
gx:horizFov
gx:interpolate
gx:labelVisibility
gx:LatLonQuad
gx:MultiTrack
gx:vieweroptions
gx:outerColor
gx:outerWidth
gx:physicalWidth
gx:Playlist
gx:playMode
gx:SoundCue
gx:TimeSpan
gx:TimeStamp
gx:Tour
gx:TourControl
gx:TourPrimitive
gx:Track
gx:ViewerOptions
gx:w
gx:Wait
gx:x
gx:y
The complete XML schema for elements in this extension namespace is
located at http://developers.google.com/kml/schema/kml22gx.xsd.
"""
# -*- coding: utf-8 -*-
"""
KML is an open standard officially named the OpenGIS KML Encoding Standard
(OGC KML). It is maintained by the Open Geospatial Consortium, Inc. (OGC).
The complete specification for OGC KML can be found at
http://www.opengeospatial.org/standards/kml/.
The complete XML schema for KML is located at
http://schemas.opengis.net/kml/.
"""
from shapely.geometry import Point, LineString, Polygon
from shapely.geometry import MultiPoint, MultiLineString, MultiPolygon
from shapely.geometry.polygon import LinearRing
import logging
logger = logging.getLogger('fastkml')
logger = logging.getLogger('fastkml.kml')
try:
......@@ -14,14 +25,24 @@ except ImportError:
import xml.etree.ElementTree as etree
LXML = False
from styles import StyleUrl, Style, StyleMap, _StyleSelector
import atom
import gx
class KML(object):
""" represents a KML File """
_features = []
ns = None
def __init__(self):
def __init__(self, ns=None):
self._features =[]
if ns == None:
self.ns = '{http://www.opengis.net/kml/2.2}'
else:
self.ns = ns
def from_string(self, xml_string):
""" create a KML object from a xml string"""
......@@ -47,7 +68,7 @@ class KML(object):
raise TypeError
def etree_element(self):
root = etree.Element('{http://www.opengis.net/kml/2.2}kml')
root = etree.Element('%skml' % self.ns)
for feature in self.features():
root.append(feature.etree_element())
return root
......@@ -59,13 +80,15 @@ class KML(object):
def features(self):
""" return a list of features """
#XXX yield feature, test if they are valid features
return self._features
def append(self, kmlobj):
""" append a feature """
if isinstance(kmlobj, (Document, Folder, Placemark)):
self._features.append(kmlobj)
else:
raise TypeError
class _Feature(object):
"""
......@@ -78,7 +101,7 @@ class _Feature(object):
#PhotoOverlay,
#ScreenOverlay
"""
ns = None
id = None
name = None
#User-defined text displayed in the 3D viewer as the label for the
......@@ -99,17 +122,41 @@ class _Feature(object):
#If the style is in the same file, use a # reference.
#If the style is defined in an external file, use a full URL
#along with # referencing.
_styles = None
#atom_author = None
#atom_link = None
#XXX atom_author = None
#XXX atom_link = None
def __init__(self, ns, id=None, name=None, description=None):
def __init__(self, ns=None, id=None, name=None, description=None,
styles=None, styleUrl=None):
self.id = id
self.name=name
self.description=description
self.ns = ns
self.styleUrl = styleUrl
self._styles = []
if styles:
for style in styles:
self.append_style(style)
if ns == None:
self.ns = '{http://www.opengis.net/kml/2.2}'
else:
self.ns = ns
def append_style(self, style):
""" append a style to the feature """
if isinstance(style, _StyleSelector):
self._styles.append(style)
else:
raise TypeError
def styles(self):
""" iterate over the styles of this feature """
for style in self._styles:
if isinstance(style, _StyleSelector):
yield style
else:
raise TypeError
def etree_element(self):
if self.__name__:
......@@ -126,6 +173,11 @@ class _Feature(object):
visibility.text = str(self.visibility)
isopen = etree.SubElement(element, "%sopen" %self.ns)
isopen.text = str(self.isopen)
if self.styleUrl:
styleUrl = StyleUrl( self.ns, self.styleUrl)
element.append(styleUrl.etree_element())
for style in self.styles():
element.append(style.etree_element())
else:
raise NotImplementedError
return element
......@@ -148,10 +200,22 @@ class _Feature(object):
self.description = description.text
visibility = element.find('%svisibility' %self.ns)
if visibility is not None:
self.visibility = visibility.text
self.visibility = int(visibility.text)
isopen = element.find('%sopen' %self.ns)
if isopen is not None:
self.isopen = isopen.text
self.isopen = int(isopen.text)
styles = element.findall('%sStyle' % self.ns)
for style in styles:
s = Style(self.ns)
s.from_element(style)
self.append_style(s)
styles = element.findall('%sStyleMap' % self.ns)
for style in styles:
s = StyleMap(self.ns)
s.from_element(style)
self.append_style(s)
class _Container(_Feature):
"""
......@@ -165,9 +229,13 @@ class _Container(_Feature):
_features = []
def __init__(self, ns, id=None, name=None, description=None):
def __init__(self, ns=None, id=None, name=None, description=None):
super(_Container, self).__init__(ns, id, name, description)
self._features =[]
if ns == None:
self.ns = '{http://www.opengis.net/kml/2.2}'
else:
self.ns = ns
def features(self):
""" return a list of features """
......@@ -230,6 +298,12 @@ class Folder(_Container):
self.append(feature)
class Placemark(_Feature):
"""
A Placemark is a Feature with associated Geometry.
In Google Earth, a Placemark appears as a list item in the Places
panel. A Placemark with a Point has an icon associated with it that
marks a point on the Earth in the 3D viewer.
"""
__name__ = "Placemark"
geometry = None
......@@ -401,4 +475,3 @@ class Placemark(_Feature):
......@@ -42,9 +42,9 @@ class BuildKmlTestCase(unittest.TestCase):
k = kml.KML()
ns = '{http://www.opengis.net/kml/2.2}'
p = kml.Placemark(ns, 'id', 'name', 'description')
p.geometry = Point(0.0, 0.0)
p.geometry = Point(0.0, 0.0, 0.0)
p2 = kml.Placemark(ns, 'id2', 'name2', 'description2')
p2.geometry = LineString([(0, 0), (1, 1)])
p2.geometry = LineString([(0, 0, 0), (1, 1, 1)])
k.append(p)
k.append(p2)
self.assertEqual(len(k.features()),2)
......@@ -65,7 +65,7 @@ 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), (1, 1), (1, 0)])
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!
f2.append(p)
......@@ -302,8 +302,7 @@ class KmlFromStringTestCase( unittest.TestCase ):
k2 = kml.KML()
k2.from_string(k.to_string())
self.assertEqual(k.to_string(), k2.to_string())
print k.to_string()
print
def test_suite():
suite = unittest.TestSuite()
......
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