Commit a2b94a48 authored by Christian Ledermann's avatar Christian Ledermann

add geometry processing

parent c9ef8d55
# -*- coding: utf-8 -*-
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')
try:
from lxml import etree
LXML = True
except ImportError:
import xml.etree.ElementTree as etree
LXML = False
class KML(object):
""" represents a KML File """
......@@ -19,7 +29,9 @@ class KML(object):
self.from_string(f.read())
f.close()
def from_string(self, xml_string):
""" create a KML object from a xml string"""
element = etree.XML(xml_string)
if element.tag.endswith('kml'):
ns = element.tag.rstrip('kml')
......@@ -68,20 +80,36 @@ class _Feature(object):
subclasses are:
Container (Document, Folder),
Placemark,
NetworkLink,
GroundOverlay,
PhotoOverlay,
ScreenOverlay
#NetworkLink,
#GroundOverlay,
#PhotoOverlay,
#ScreenOverlay
"""
id = None
name = None
#User-defined text displayed in the 3D viewer as the label for the
#object (for example, for a Placemark, Folder, or NetworkLink).
description = None
#User-supplied content that appears in the description balloon.
visibility = 1
#Boolean value. Specifies whether the feature is drawn in the 3D
#viewer when it is initially loaded. In order for a feature to be
#visible, the <visibility> tag of all its ancestors must also be
#set to 1.
isopen = 0
#Boolean value. Specifies whether a Document or Folder appears
#closed or open when first loaded into the Places panel.
#0=collapsed (the default), 1=expanded.
styleUrl = None
#URL of a <Style> or <StyleMap> defined in a Document.
#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.
#atom_author = None
#atom_link = None
#styleUrl = None
def __init__(self, ns, id=None, name=None, description=None):
......@@ -155,7 +183,6 @@ class _Container(_Feature):
def etree_element(self):
element = super(_Container, self).etree_element()
for feature in self.features():
assert(feature != self)
element.append(feature.etree_element())
return element
......@@ -192,7 +219,7 @@ class Document(_Container):
class Folder(_Container):
"""
A Folder is used to arrange other Features hierarchically
(Folders, Placemarks, NetworkLinks, or Overlays).
(Folders, Placemarks, #NetworkLinks, or #Overlays).
"""
__name__ = "Folder"
......@@ -210,9 +237,175 @@ class Folder(_Container):
self.append(feature)
class Placemark(_Feature):
__name__ = "Placemark"
styleUrl = None
geometry = None
pass
def _get_coordinates(self, element):
coordinates = element.find('%scoordinates' % self.ns)
if coordinates is not None:
latlons = coordinates.text.strip().split()
coords = []
for latlon in latlons:
coords.append([float(c) for c in latlon.split(',')])
return coords
def _get_linear_ring(self, element):
# LinearRing
lr = element.find('%sLinearRing' % self.ns)
if lr is not None:
coords = self._get_coordinates(lr)
return LinearRing(coords)
def _get_geometry(self, element):
# Point, LineString,
# Polygon,
point = element.find('%sPoint' % self.ns)
if point is not None:
coords = self._get_coordinates(point)
return Point(coords[0])
line = element.find('%sLineString' % self.ns)
if line is not None:
coords = self._get_coordinates(line)
return LineString(coords)
polygon = element.find('%sPolygon' % self.ns)
if polygon is not None:
outer_boundary = polygon.find('%souterBoundaryIs' %self.ns)
ob = self._get_linear_ring(outer_boundary)
inner_boundaries = polygon.findall('%sinnerBoundaryIs' %self.ns)
ibs = []
for inner_boundary in inner_boundaries:
ibs.append(self._get_linear_ring(inner_boundary))
return Polygon(ob, ibs)
return self._get_linear_ring(element)
def _get_multigeometry(self, element):
# MultiGeometry
multigeometry = element.find('%sMultiGeometry' % self.ns)
geoms = []
if multigeometry is not None:
points = multigeometry.findall('%sPoint' % self.ns)
if points:
for point in points:
geoms.append(Point(self._get_coordinates(point)))
return MultiPoint(geoms)
linestrings = multigeometry.findall('%sLineString' % self.ns)
if linestrings:
for ls in linestrings:
geoms.append(LineString(self._get_coordinates(ls)))
return MultiLineString(geoms)
polygons = multigeometry.findall('%sPolygon' % self.ns)
if polygons:
for polygon in polygons:
outer_boundary = polygon.find('%souterBoundaryIs' %self.ns)
ob = self._get_linear_ring(outer_boundary)
inner_boundaries = polygon.findall('%sinnerBoundaryIs' %self.ns)
ibs = []
for inner_boundary in inner_boundaries:
ibs.append(self._get_linear_ring(inner_boundary))
geoms.append(Polygon(ob, ibs))
return MultiPolygon(geoms)
def from_element(self, element):
super(Placemark, self).from_element(element)
mgeom = self._get_multigeometry(element)
geom = self._get_geometry(element)
if mgeom is not None:
self.geometry = mgeom
elif geom is not None:
self.geometry = geom
else:
logger.warn('No geometries found')
def _etree_coordinates(self, coordinates):
element = etree.Element("coordinates")
if len(coordinates[0]) == 2:
tuples = ('%f,%f,0.0' % tuple(c) for c in coordinates)
elif len(coordinates[0]) == 3:
tuples = ('%f,%f,%f' % tuple(c) for c in coordinates)
else:
raise ValueError("Invalid dimensions")
element.text = ' '.join(tuples)
return element
def _etree_point(self, point):
element = etree.Element("Point")
coords = list(point.coords)
element.append(self._etree_coordinates(coords))
return element
def _etree_linestring(self, linestring):
element = etree.Element("LineString")
coords = list(linestring.coords)
element.append(self._etree_coordinates(coords))
return element
def _etree_linearring(self, linearring):
element = etree.Element("LinearRing")
coords = list(linearring.coords)
element.append(self._etree_coordinates(coords))
return element
def _etree_polygon(self, polygon):
element = etree.Element("Polygon")
outer_boundary = etree.SubElement(element, "outerBoundaryIs")
outer_boundary.append(self._etree_linearring(polygon.exterior))
for ib in polygon.interiors:
inner_boundary = etree.SubElement(element, "innerBoundaryIs")
inner_boundary.append(self._etree_linearring(ib))
return element
def _etree_multipoint(self, points):
element = etree.Element("MultiGeometry")
for point in points.geoms:
element.append(self._etree_point(point))
return element
def _etree_multilinestring(self, linestrings):
element = etree.Element("MultiGeometry")
for linestring in linestrings.geoms:
element.append(self._etree_linestring(linestring))
return element
def _etree_multipolygon(self, polygons):
element = etree.Element("MultiGeometry")
for polygon in polygons.geoms:
element.append(self._etree_polygon(polygon))
return element
def _etree_geometry(self):
if isinstance(self.geometry, Point):
return self._etree_point(self.geometry)
elif isinstance(self.geometry, LineString):
return self._etree_linestring(self.geometry)
elif isinstance(self.geometry, LinearRing):
return self._etree_linearring(self.geometry)
elif isinstance(self.geometry, Polygon):
return self._etree_polygon(self.geometry)
elif isinstance(self.geometry, MultiPoint):
return self._etree_multipoint(self.geometry)
elif isinstance(self.geometry, MultiLineString):
return self._etree_multilinestring(self.geometry)
elif isinstance(self.geometry, MultiPolygon):
return self._etree_multipolygon(self.geometry)
def etree_element(self):
element = super(Placemark, self).etree_element()
if self.geometry is not None:
element.append(self._etree_geometry())
else:
logger.warn('Object does not have a geometry')
return element
This diff is collapsed.
......@@ -19,6 +19,7 @@ Create and read KML Files""",
zip_safe=False,
install_requires=[
# -*- Extra requirements: -*-
'shapely',
],
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