2

I am looking at a way to migrate the metadata from a spreadsheet into ISO 19139 compliant XML metadata documents for a collection of shapefiles. I can parse the data out of the spreadsheet into strings without issue however I am getting lost on how to take those strings and use them to populate the elements.

This example: http://pymotw.com/2/xml/etree/ElementTree/create.html covers most of what I want to do, however I would need to generate the tree beforehand based on the ISO 19139 schema. PyXB seems to have schema bundles for OpenGIS but I would like some insight on how to connect everything.

1 Answer 1

3

One way of doing it is to generate a really simple XML tree for each record in your spreadsheet, write an XSLT stylesheet to translate from your simple XML to ISO 19139, then use lxml.etree.XSLT to transform.

The major advantage of this is it keeps the complex iso19139 XML out of your python code which makes it much easier to debug. Some more info/tutorials on xslt here and here.

Here's some example code:

from lxml import etree
import os.path

def list_to_xml(datalist,rootname):
    '''Transform a metadata record to a flat XML string'''

    root = etree.Element(rootname)

    for fld in datalist:
        col=fld[0]
        dat=str(fld[1]) # may need to be careful of unicode encoding issues
                        # when reading data from Excel

        child=etree.SubElement(root,col)
        child.text=dat

    return root

def transform(inxml,xslfile):
    xslfile=os.path.abspath(xslfile).replace('\\','/') #xslt doesn't like backslashes in absolute paths...
    xsl = etree.parse(xslfile)
    xslt = etree.XSLT(xsl)
    return xslt(inxml)

xslfile = 'test.xslt'

#Column headers from the Excel spreadsheet
header=['column1','xmin','ymin','xmax','ymax','thelastcolumn']

#Data pulled out of the spreadsheet
#You would loop over all rows and do a transform on each row
row=['A value',1.2345,5.4321,2.3456,6.5432,'last value']

inxml = list_to_xml(zip(header,row),"ExcelMetadata")
print etree.tostring(inxml)

#The above prints - <ExcelMetadata><column1>A value</column1><xmin>1.2345</xmin><ymin>5.4321</ymin><xmax>2.3456</xmax><ymax>6.5432</ymax><thelastcolumn>last value</thelastcolumn></ExcelMetadata>

iso19139 = transform(inxml,xslfile)
print str(iso19139)

The 'test.xslt' stylesheet referred to in the above code is:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"   
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:fn="http://www.w3.org/2005/xpath-functions" 
  xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" 
  xmlns:gco="http://www.isotc211.org/2005/gco" 
  xmlns:gmd="http://www.isotc211.org/2005/gmd" 
  xmlns:xlink="http://www.w3.org/1999/xlink" 
  xmlns:gts="http://www.isotc211.org/2005/gts" 
  xmlns:gsr="http://www.isotc211.org/2005/gsr" 
  xmlns:gss="http://www.isotc211.org/2005/gss" 
  xmlns:gmx="http://www.isotc211.org/2005/gmx" 
  xmlns:gml="http://www.opengis.net/gml" 
  xsi:schemaLocation="http://www.isotc211.org/2005/gmd http://www.isotc211.org/2005/gmd/gmd.xsd">

  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match="/ExcelMetadata">

      <gmd:fileIdentifier>
        <gco:CharacterString><xsl:value-of select="column1"/></gco:CharacterString>
      </gmd:fileIdentifier>

      <!-- Lots of other xml stuff -->

      <xsl:if test="normalize-space(thelastcolumn)">
        <gmd:someelement>
          <gco:CharacterString><xsl:value-of select="thelastcolumn"/></gco:CharacterString>
        </gmd:someelement>
      </xsl:if>

      <gmd:identificationInfo>
        <gmd:MD_DataIdentification>
          <gmd:extent>
            <gmd:EX_Extent>
              <gmd:geographicElement>
                <gmd:EX_GeographicBoundingBox>
                  <gmd:extentTypeCode>
                    <gco:Boolean>1</gco:Boolean>
                  </gmd:extentTypeCode>
                  <gmd:westBoundLongitude>
                    <gco:Decimal><xsl:value-of select="xmin"/></gco:Decimal>
                  </gmd:westBoundLongitude>
                  <gmd:eastBoundLongitude>
                    <gco:Decimal><xsl:value-of select="xmax"/></gco:Decimal>
                  </gmd:eastBoundLongitude>
                  <gmd:southBoundLatitude>
                    <gco:Decimal><xsl:value-of select="ymin"/></gco:Decimal>
                  </gmd:southBoundLatitude>
                  <gmd:northBoundLatitude>
                    <gco:Decimal><xsl:value-of select="ymax"/></gco:Decimal>
                  </gmd:northBoundLatitude>
                </gmd:EX_GeographicBoundingBox>
              </gmd:geographicElement>
            </gmd:EX_Extent>
          </gmd:extent>
        </gmd:MD_DataIdentification>
      </gmd:identificationInfo>

    </xsl:template>
</xsl:stylesheet>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.