pythonlxmlkmlobjectifypykml

checking and fixing values appended to a node with lxml.objectify in python


I'm trying to modify PyKML, which uses lxml.objectify. With a track node append, this gets turned into a string with the default str() behavior. I'd like to catch appends of lists or tuples and convert them to proper place separated lines rather than '(xx.xxxx, yy.yyyy)'

from pykml.factory import GX_ElementMaker as GX

track = GX.Track(id='track_%d' % group_num )
for pt in group:
    when = datetime.datetime.utcfromtimestamp(pt['ts'])
    track.append( KML.when( when ) )  # WHEN?
for pt in group:
    track.append( GX.coord( (pt['x'],pt['y'])) )  # <-- trouble here

Thanks, -kurt


Solution

  • Create a clean ElementMaker class with the namespace info. Then create a subclass with the node name as a method. In that method, handle all the odd cases. Then craft the string that would go in that node names place and return an instance of the clean ElementMaker class with that node name.

    http://code.google.com/r/schwehr-pykml/source/browse/src/pykml/factory.py?spec=svn05a10cef3fd3c430389e8aca1313a20da932e565&r=05a10cef3fd3c430389e8aca1313a20da932e565

    def indexable_levels(args):
        #print 'args:',args
        levels = 0
        while True:
            if isinstance(args,str): break
            try:
                args = args[0]
                levels += 1
            except:
                break
        #print '   levels ->',levels
        return levels
    
    # Create a factory object for the KML Google Extension namespace
    _GX_ElementMakerSimple = objectify.ElementMaker(
        annotate=False,
        namespace=nsmap['gx'],
        nsmap={'gx': nsmap['gx']},
    )
    
    class _GX_ElementMaker (objectify.ElementMaker):
        'KML ElementMaker with overloads for custom text payloads like coordinates'
        def coord(self, *args):
            #print 'start coord: "%s"' % (str(args)), type(args), len(args)
            levels = indexable_levels(args)
            if levels == 1 and len(args) == 1:
                # This case is really redundant with the next
                assert isinstance(args[0],str)
                return _GX_ElementMakerSimple.coord(args[0])
            if levels == 1:
                return _GX_ElementMakerSimple.coord(' '.join([str(item) for item in args]))
            if levels == 2:
                # ((-121.583851, 37.386052),)
                assert(len(args)==1)
                return _GX_ElementMakerSimple.coord(' '.join([str(item) for item in args[0] ]))
            assert(False)
    
    # Create a factory object for the KML Google Extension namespace
    GX_ElementMaker = _GX_ElementMaker(
        annotate=False,
        namespace=nsmap['gx'],
        nsmap={'gx': nsmap['gx']},
    )