Source code for upoints.kml

#
# coding=utf-8
"""kml - Imports KML data files."""
# Copyright © 2007-2017  James Rowe <jnrowe@gmail.com>
#
# This file is part of upoints.
#
# upoints is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# upoints 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# upoints.  If not, see <http://www.gnu.org/licenses/>.

import logging

from lxml import etree

from . import (point, trigpoints, utils)

KML_NS = 'http://earth.google.com/kml/2.2'
etree.register_namespace('kml', KML_NS)

create_elem = utils.element_creator(KML_NS)


[docs]class Placemark(trigpoints.Trigpoint): """Class for representing a Placemark element from KML data files. .. versionadded:: 0.6.0 """ __slots__ = ('description', ) def __init__(self, latitude, longitude, altitude=None, name=None, description=None): """Initialise a new ``Placemark`` object. Args: latitude (float): Placemarks's latitude longitude (float): Placemark's longitude altitude (float): Placemark's altitude name (str): Name for placemark description (str): Placemark's description """ super(Placemark, self).__init__(latitude, longitude, altitude, name) if altitude: self.altitude = float(altitude) self.description = description def __str__(self): """Pretty printed location string. Returns: str: Human readable string representation of ``Placemark`` object """ location = super(Placemark, self).__format__('dms') if self.description: return '%s [%s]' % (location, self.description) else: return location
[docs] def tokml(self): """Generate a KML Placemark element subtree. Returns: etree.Element: KML Placemark element """ placemark = create_elem('Placemark') if self.name: placemark.set('id', self.name) placemark.name = create_elem('name', text=self.name) if self.description: placemark.description = create_elem('description', text=self.description) placemark.Point = create_elem('Point') data = [str(self.longitude), str(self.latitude)] if self.altitude: if int(self.altitude) == self.altitude: data.append('%i' % self.altitude) else: data.append(str(self.altitude)) placemark.Point.coordinates = create_elem('coordinates', text=','.join(data)) return placemark
[docs]class Placemarks(point.KeyedPoints): """Class for representing a group of :class:`Placemark` objects. .. versionadded:: 0.6.0 """ def __init__(self, kml_file=None): """Initialise a new ``Placemarks`` object.""" super(Placemarks, self).__init__() self._kml_file = kml_file if kml_file: self.import_locations(kml_file)
[docs] def import_locations(self, kml_file): """Import KML data files. ``import_locations()`` returns a dictionary with keys containing the section title, and values consisting of :class:`Placemark` objects. It expects data files in KML format, as specified in `KML Reference`_, which is XML such as:: <?xml version="1.0" encoding="utf-8"?> <kml xmlns="http://earth.google.com/kml/2.1"> <Document> <Placemark id="Home"> <name>Home</name> <Point> <coordinates>-0.221,52.015,60</coordinates> </Point> </Placemark> <Placemark id="Cambridge"> <name>Cambridge</name> <Point> <coordinates>0.390,52.167</coordinates> </Point> </Placemark> </Document> </kml> The reader uses the :mod:`ElementTree` module, so should be very fast when importing data. The above file processed by ``import_locations()`` will return the following ``dict`` object:: {"Home": Placemark(52.015, -0.221, 60), "Cambridge": Placemark(52.167, 0.390, None)} Args: kml_file (iter): KML data to read Returns: dict: Named locations with optional comments .. _KML Reference: http://code.google.com/apis/kml/documentation/kmlreference.html """ self._kml_file = kml_file data = utils.prepare_xml_read(kml_file, objectify=True) for place in data.Document.Placemark: name = place.name.text coords = place.Point.coordinates.text if coords is None: logging.info('No coordinates found for %r entry' % name) continue coords = coords.split(',') if len(coords) == 2: longitude, latitude = coords altitude = None elif len(coords) == 3: longitude, latitude, altitude = coords else: raise ValueError('Unable to handle coordinates value %r' % coords) try: description = place.description except AttributeError: description = None self[name] = Placemark(latitude, longitude, altitude, name, description)
[docs] def export_kml_file(self): """Generate KML element tree from ``Placemarks``. Returns: etree.ElementTree: KML element tree depicting ``Placemarks`` """ kml = create_elem('kml') kml.Document = create_elem('Document') for place in sorted(self.values(), key=lambda x: x.name): kml.Document.append(place.tokml()) return etree.ElementTree(kml)