[python] Порка небольшой обертки для записи kml файлов.
От: CiViLiS Россия  
Дата: 12.06.10 23:12
Оценка:
Прошу выпороть код не большой обертки для создания kml файлов. Основная цель -- сделать создание kml файлов наиболее простым и наглядным способом, правда знания как устроен kml при этом необходимы.
Исходники врапера, примера использования и генирируемого файла можно взять из архива
Сначала как использовать:
# -*- coding: cp1251 -*-
import kmlwriter

def test_kml():
    writer = kmlwriter.KMLWriter("test_placemark.kml")
    doc = kmlwriter.Document() # создаем документ
    writer.set_data(doc) # делаем документ главным элементов в kml файле
    doc.name = 'test_placemark' # добавляем свойство name

    f = doc.add(kmlwriter.Folder()) # добавляем папку в документ
    f.name = "folder name" # добавляем свойство name

    pm = f.add(kmlwriter.Placemark()) # добавляем маркер
    pm.add(kmlwriter.Point(55.750871,37.614076,0)) # добавляем свойство point с координатами
    pm.name = "Kremlin" 
    pm.description = kmlwriter.CDATA("""<b>multiline description</b><br>
    line1<br>
    line2<br>
    <b>test ampersand and other special marks:</b> &'!" <br>
    """)

    pm = doc.add(kmlwriter.Placemark())
    line = [(37.80666418607323,-122.4425587930444,0),
             (37.80663578323093,-122.4428379594768,0)]
    pm.add(kmlwriter.LineString(line)) # добавляем линию
    pm.name = "line1"
    
    circlestyle = doc.add(kmlwriter.Style('circle_style')) # создаем новые стиль
    circlestyle.add(kmlwriter.LineStyle(color="FF0000FF")) # добавляем стиль для линий
    circlestyle.add(kmlwriter.PolyStyle(color="3300FF00", id="qwe")) # добавляем стиль для рисования многоугольника

    sectorstyle = doc.add(kmlwriter.Style('sector_style')) # создаем еще один стиль
    sectorstyle.add(kmlwriter.LineStyle(color="FFFF0000"))
    sectorstyle.add(kmlwriter.PolyStyle(color="FF0000FF"))
    
    # добавляем точку-окружность 
    pm = doc.add(kmlwriter.Placemark())
    pm.name = "test_circle placemark"
    pm.styleUrl = circlestyle
    pm.add(kmlwriter.circle_polygon(45.608274,-73.731995, 7000))


    folder = doc.add(kmlwriter.Folder())
    folder.name = "sectors"
    for lat in xrange(-80, 81, 5):
        for lon in xrange(-170, 171, 5):
            pm = folder.add(kmlwriter.Placemark())
            pm.name = "sector %s %s" % (lat, lon)
            pm.add(kmlwriter.sector_polygon(lat,lon, 90000, -15, 15, 10, 10))
            pm.styleUrl = sectorstyle

    writer.save() # сохраняем в файл

if __name__ == "__main__":
    test_kml()


Ну и сам код:
  Скрытый текст
from math import *

RADIUS_EARTH_METERS = 6366197.

def move(lat, lon, d, tc):
    """ Moves point to d meters. tc is azimut of moving """
    lat = radians(lat)
    lon = radians(lon)
    d = d/RADIUS_EARTH_METERS
    tc  = radians(-tc)

    def mod(y,x):
        return y - x*floor(y/x)

    la = asin(sin(lat)*cos(d)+cos(lat)*sin(d)*cos(tc))
    if cos(la) == 0:
        lo = lon
    else:
        lo = lon-asin(sin(tc)*sin(d)/cos(la))
        lo = mod(lo+pi,2*pi)-pi
    return (degrees(la),degrees(lo))

class KMLElement(object):
    def __init__(self, name = None, attributes = None, content = None):
        if name is None:
            name = self.__class__.__name__
        self._name = name
        self._attributes = attributes
        self._content = None
        if isinstance(content, dict):
            for k,v in content.items():
                self.add(KMLElement(k, None, v))
        else:
            self._content = content

    def is_simple(self):
        return not isinstance(self._content, list)

    def add(self, element):
        if self._content is None:
            self._content = [element]
            return element
        if isinstance(self._content, str):
            raise Exception("Can't add new subelemet to '%s', bacause it already has string content '%s'" % (self._name, self._content))
        self._content.append(element)
        return element

    @staticmethod
    def _special_handling(name, value):
        if name == 'styleUrl':
            if isinstance(value, str):
                return value
            if isinstance(value, Style):
                return "#"+value._attributes['id']
            else:
                raise Exception('invalid values used as Style')
        return value

    def __setattr__(self, name, value):
        if name.startswith('_'):
            self.__dict__[name] = value
        else:
            e = KMLElement(name, None, self._special_handling(name, value))
            self.add(e)
            return e

    def __str__(self):
        if self._content is None:
            c = None
        elif isinstance(self._content, list):
            c = ";".join([str(e) for e in self._content])
        else:
            c = str(self._content)
        return "Element %s attributes: %s, content: [%s]" % (self._name, self._attributes, c)

    def find(self, name):
        if not isinstance(self._content, list):
            return
        for e in self._content:
            if e._name == name:
                yield e
            for ee in e.find(name):
                yield ee

class Document(KMLElement):
    def __init__(self):
        super(Document, self).__init__()


class Folder(KMLElement):
    def __init__(self):
        super(Folder, self).__init__()


class Style(KMLElement):
    def __init__(self, id):
        super(Style, self).__init__(attributes={'id':id})


class _StyleBase(KMLElement):
    def __init__(self, kw):
        attributes = None
        if 'id' in kw:
            attributes=dict(id=kw['id'])
            del kw['id']
        super(_StyleBase, self).__init__(attributes=attributes, content=kw)


class IconStyle(_StyleBase):
    def __init__(self, href = None, **kw):
        super(IconStyle, self).__init__(kw)
        if href:
            self.add(Icon(href))

class Icon(KMLElement):
    def __init__(self, href):
        super(Icon, self).__init__()
        self.href = href


class LabelStyle(_StyleBase):
    def __init__(self, **kw):
        super(LabelStyle, self).__init__(kw)


class LineStyle(_StyleBase):
    def __init__(self, **kw):
        super(LineStyle, self).__init__(kw)


class PolyStyle(_StyleBase):
    def __init__(self, **kw):
        super(PolyStyle, self).__init__(kw)


class BalloonStyle(_StyleBase):
    def __init__(self, **kw):
        super(BalloonStyle, self).__init__(kw)


class ListStyle(_StyleBase):
    def __init__(self, **kw):
        super(ListStyle, self).__init__(kw)


class Placemark(KMLElement):
    def __init__(self):
        super(Placemark, self).__init__()

class Point(KMLElement):
    def __init__(self, lat, lon, alt = 0):
        super(Point, self).__init__()
        self.coordinates = "%f,%f,%f" % (lon, lat, alt)

class LookAt(KMLElement):
    def __init__(self, lat, lon, range, **kw):
        super(LookAt, self).__init__(content=kw)
        self.latitude = lat
        self.longitude= lon
        self.range    = range

def _add_alt(p, alt):
    if len(p) == 2:
        return (p[1], p[0], alt)
    else:
        return (p[1], p[0], p[2])

class LineString(KMLElement):
    def __init__(self, points, alt = 0):
        super(LineString, self).__init__('LineString', None, None)
        self.coordinates = "\n".join("%f,%f,%f" % _add_alt(p, alt) for p in points)


class LinearRing(KMLElement):
    def __init__(self, coordinates,  alt = 0):
        super(LinearRing, self).__init__('LinearRing', None, None)
        self.coordinates = "\n".join(["%f,%f,%f" % _add_alt(p, alt) for p in coordinates])


class Polygon(KMLElement):
    def __init__(self, outerboundary, innerboundary = None,  alt = 0):
        super(Polygon, self).__init__('Polygon', None, None)
        self.extrude = 0
        self.altitudeMode = 'clampToGround'
        if innerboundary:
            boundary = self.add(KMLElement('innerBoundaryIs', None, None))
            boundary.add(LinearRing(innerboundary, alt))
        if outerboundary:
            boundary = self.add(KMLElement('outerBoundaryIs', None, None))
            boundary.add(LinearRing(outerboundary, alt))


def circle_polygon(lat, lon, radius, n = 36, alt = 0):
    points = []
    for angle in xrange(0, 360, 360/n):
        points.append(move(lat, lon, radius, angle))
    return Polygon(points, alt = alt)

def sector_polygon(lat, lon, radius, angle1, angle2, n, alt = 0):
    points = [(lat,lon,alt)]
    d = angle2-angle1
    for x in xrange(0, n):
        angle = angle1 + d*x/(n-1)

        points.append(move(lat, lon, radius, angle))
    points.append((lat,lon))
    return Polygon(points, alt = alt)

class CDATA(object):
    def __init__(self, s):
        self.s = s

    def get(self):
        return "<![CDATA[" + self.s + "]]>"

class KMLWriter:
    _filename = None
    _file = None
    _encoding = "UTF-8"
    _escapes  = [('&', '&amp;'), ('"', '&quot;'), ('<', '&lt;'), ('>', '&gt;>'), ("'", '&apos;')]
    _content = None

    def __init__(self, filename):
        self._filename = filename

    def __del__(self):
        if self._file:
            self._file.close()
            self._file = None

    def _write(self, line):
        self._file.write(line.encode(self._encoding))

    def _current_indention(self):
        return ' ' * (self._indention*4)
   
    def _writeln(self, line):
        line = self._current_indention() + line + '\n'
        self._write(line)

    def set_data(self, data):
        self._content = data
        return data

    def save(self):
        self._file = open(self._filename, "w")
        self._indention = 0
        self._writeln('<?xml version="1.0" encoding="%s"?>' % self._encoding)
        self._writeln('<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">')
        if self._content:
            self._save_element(self._content)
        self._writeln('</kml>')
        self._file.close()
        del(self._indention)

    def escape(self, line):
        if isinstance(line, CDATA):
            return line.get()
        else:
            line = str(line)
            for c,r in self._escapes:
                line = line.replace(c, r)
            return line

    def reindent(self, s):
        s = s.split('\n')
        s = [self._current_indention() + line.lstrip() for line in s]
        s[0] = s[0].lstrip()
        return '\n'.join(s)

    def _save_simple_element(self, name, content):
        s = self.escape(content)
        if s.find('\n') != -1:
            self._writeln("<%s>" % name)
            self._indention += 1
            self._writeln(self.reindent(s))
            self._indention -= 1
            self._writeln("</%s>" % name)
        else:
            s = "<%s>%s</%s>" % (name, s, name)
            self._writeln(s)

    def _start_tag(self, name, attributes):
        if attributes:
            if isinstance(attributes, str):
                attr = attributes
            elif isinstance(attributes, dict):
                attr = " ".join(['%s="%s"' % (k,self.escape(v)) for k,v in attributes.iteritems()])
            else:
                raise Exception("attributes should be str or dict, not %s" % type(attributes).__name__)
            return "<%s %s>" % (name, attr)
        else:
            return "<%s>" % name

    def _save_element(self, element):
        if element._content is None:
            raise Exception("Element %s has empty content" % element)
        if element.is_simple():
            self._save_simple_element(element._name, element._content)
            return
        self._writeln(self._start_tag(element._name, element._attributes))
        self._indention += 1
        for e in element._content:
            self._save_element(e)
        self._indention -= 1
        self._writeln("</%s>" % element._name)

    def find(self, name):
        for f in self._content.find(name):
            yield f
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
"Бог не терпит голой сингулярности" -- Роджер Пенроуз
Re: [python] Порка небольшой обертки для записи kml файлов.
От: Temoto  
Дата: 13.06.10 07:22
Оценка:
CVL>Прошу выпороть код не большой обертки для создания kml файлов. Основная цель -- сделать создание kml файлов наиболее простым и наглядным способом, правда знания как устроен kml при этом необходимы.
CVL>Исходники врапера, примера использования и генирируемого файла можно взять из архива
CVL>Сначала как использовать:

Я по поводу мелочей только...

# coding: cp1251
так короче. -*- необязательно
И у вас 6 ошибок в слове utf-8

CVL> writer.save() # сохраняем в файл

Подобных комментариев а-ля Капитан Очевидность у вас подавляющее большинство. Объяснять надо то, что вызывает удивление или объяснять хитрую логику. Согласны? Простые вещи понятны из кода.

А вот большой файл, с кучей логики, наоборот, недокументирован.
Re: [python] Порка небольшой обертки для записи kml файлов.
От: HiSH Россия http://m0riarty.ya.ru
Дата: 13.06.10 09:40
Оценка: 1 (1)
Здравствуйте, CiViLiS, Вы писали:

CVL>Прошу выпороть код не большой обертки для создания kml файлов. Основная цель -- сделать создание kml файлов наиболее простым и наглядным способом, правда знания как устроен kml при этом необходимы.

CVL>Исходники врапера, примера использования и генирируемого файла можно взять из архива
CVL>Сначала как использовать:
CVL>
...
CVL>    pm = f.add(kmlwriter.Placemark()) # добавляем маркер
CVL>    pm.add(kmlwriter.Point(55.750871,37.614076,0)) # добавляем свойство point с координатами
CVL>    pm.name = "Kremlin" 
CVL>    pm.description = kmlwriter.CDATA("""<b>multiline description</b><br>
CVL>    line1<br>
CVL>    line2<br>
CVL>    <b>test ampersand and other special marks:</b> &'!" <br>
CVL>    """)
...
CVL>


ИМХО, слишком многословно получается. Я бы подумал о передаче атрибутов элемента прямо при его создании:
f.add(kmlwriter.Placemark(name = "Kremlin", description  = "", points = [kmlwriter.Point(...)]))


Ну и делать XML руками как минимум негигиенично. Обертка вокруг готовой библиотеки получится меньше и лучше.
Re[2]: [python] Порка небольшой обертки для записи kml файло
От: HiSH Россия http://m0riarty.ya.ru
Дата: 13.06.10 09:44
Оценка:
Здравствуйте, HiSH, Вы писали:

HSH>Здравствуйте, CiViLiS, Вы писали:


HSH>Ну и делать XML руками как минимум негигиенично. Обертка вокруг готовой библиотеки получится меньше и лучше.


Кстати, посмотрите, как в lxml сделан TreeBuilder. Сам я такое не использовал, посоветовать не могу, но вполне может оказаться, что такой подход удобен: http://codespeak.net/lxml/tutorial.html#the-e-factory
Re[3]: [python] Порка небольшой обертки для записи kml файло
От: CiViLiS Россия  
Дата: 13.06.10 14:53
Оценка:
Здравствуйте, HiSH, Вы писали:

HSH>>Ну и делать XML руками как минимум негигиенично. Обертка вокруг готовой библиотеки получится меньше и лучше.

Ну в этом то и цель.. чтобы просто и быстро. Хотя надо действительно посмотреть, может действительно какие нить бенифиты получу если готовую библиотеку для xml использовать.

HSH>Кстати, посмотрите, как в lxml сделан TreeBuilder. Сам я такое не использовал, посоветовать не могу, но вполне может оказаться, что такой подход удобен: http://codespeak.net/lxml/tutorial.html#the-e-factory

Спасибо, посмотрю.. Что то подобное и я хотел сделать..

HSH>ИМХО, слишком многословно получается. Я бы подумал о передаче атрибутов элемента прямо при его создании:

HSH>f.add(kmlwriter.Placemark(name = "Kremlin", description = "", points = [kmlwriter.Point(...)]))
Хм, в начале я тоже такое же хотел, оно даже частично работает.. Но забыл и не до пилил Спасибо что напомнили.. пойду допиливать.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
"Бог не терпит голой сингулярности" -- Роджер Пенроуз
Re[2]: [python] Порка небольшой обертки для записи kml файло
От: CiViLiS Россия  
Дата: 13.06.10 14:53
Оценка:
Здравствуйте, Temoto, Вы писали:

T># coding: cp1251

T>так короче. -*- необязательно
T>И у вас 6 ошибок в слове utf-8
Я это добавил, как и очевиные каменты только чтобы сюда выложить.. Ок, больше не буду

T>А вот большой файл, с кучей логики, наоборот, недокументирован.

это да.. это одна из проблем всех собственных велосипедов..
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
"Бог не терпит голой сингулярности" -- Роджер Пенроуз
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.