PostGIS – Saving XML CityModel Files to PostGIS Using Python

postgispostgresqlpythonxml

I'm trying to save CityModel xml files to our PostGIS Database with the files looking like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<core:CityModel xmlns:xal="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" xmlns:bldg="http://www.opengis.net/citygml/building/1.0" xmlns:gml="http://www.opengis.net/gml" xmlns:core="http://www.opengis.net/citygml/1.0" xmlns:gen="http://www.opengis.net/citygml/generics/1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/citygml/building/1.0 http://repository.gdi-de.org/schemas/adv/citygml/building/1.0/buildingLoD1.xsd http://www.opengis.net/citygml/1.0 http://repository.gdi-de.org/schemas/adv/citygml/1.0/cityGMLBaseLoD1.xsd http://www.opengis.net/citygml/generics/1.0 http://repository.gdi-de.org/schemas/adv/citygml/generics/1.0/genericsLoD1.xsd">
  <gml:name>LoD1_466_5894_2_HB</gml:name>
  <gml:boundedBy>
    <gml:Envelope srsName="urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH" srsDimension="3">
      <gml:lowerCorner srsDimension="3">466000.000 5894000.000 0.000</gml:lowerCorner>
      <gml:upperCorner srsDimension="3">468000.000 5896000.000 149.135</gml:upperCorner>
    </gml:Envelope>
  </gml:boundedBy>
  <core:cityObjectMember>
    <bldg:Building gml:id="DEHB01ALg0001dlo">
      <core:creationDate>2018-03-01</core:creationDate>
      <core:externalReference>
        <core:informationSystem>http://repository.gdi-de.org/schemas/adv/citygml/fdv/art.htm#_9100</core:informationSystem>
        <core:externalObject>
          <core:name>DEHB01ALg0001dlo</core:name>
        </core:externalObject>
      </core:externalReference>
      <gen:stringAttribute name="Gemeindeschluessel">
        <gen:value>04011000</gen:value>
      </gen:stringAttribute>
      <gen:stringAttribute name="DatenquelleLage">
        <gen:value>1000</gen:value>
      </gen:stringAttribute>
      <gen:stringAttribute name="DatenquelleDachhoehe">
        <gen:value>5000</gen:value>
      </gen:stringAttribute>
      <gen:stringAttribute name="DatenquelleBodenhoehe">
        <gen:value>1100</gen:value>
      </gen:stringAttribute>
      <gen:stringAttribute name="BezugspunktDach">
        <gen:value>2200</gen:value>
      </gen:stringAttribute>
      <bldg:function>31001_2723</bldg:function>
      <bldg:measuredHeight uom="urn:adv:uom:m">3.020</bldg:measuredHeight>
      <bldg:lod1Solid>
        <gml:Solid gml:id="UUID_6dd2f06d-e4a6-4a7c-8369-ca58ca1868b9">
          <gml:exterior>
            <gml:CompositeSurface gml:id="UUID_e0a8ff62-b627-47fd-b7f2-ef6b15bf791e">
              <gml:surfaceMember>
                <gml:Polygon gml:id="UUID_2899ca4e-bb22-4975-bfa3-0d9d9eaa7abe">
                  <gml:exterior>
                    <gml:LinearRing gml:id="UUID_2899ca4e-bb22-4975-bfa3-0d9d9eaa7abe_0_">
                      <gml:posList srsDimension="3">466921.770 5895895.491 3.020 466923.833 5895896.411 3.020 466922.753 5895898.829 3.020 466920.691 5895897.909 3.020 466921.770 5895895.491 3.020</gml:posList>
                    </gml:LinearRing>
                  </gml:exterior>
                </gml:Polygon>
              </gml:surfaceMember>
              <gml:surfaceMember>
                <gml:Polygon gml:id="UUID_7678c6ed-00a6-4ed4-9413-0d648ad00758">
                  <gml:exterior>
                    <gml:LinearRing gml:id="UUID_7678c6ed-00a6-4ed4-9413-0d648ad00758_0_">
                      <gml:posList srsDimension="3">466920.691 5895897.909 0.000 466921.770 5895895.491 0.000 466921.770 5895895.491 3.020 466920.691 5895897.909 3.020 466920.691 5895897.909 0.000</gml:posList>
                    </gml:LinearRing>
                  </gml:exterior>
                </gml:Polygon>
              </gml:surfaceMember>
              <gml:surfaceMember>
                <gml:Polygon gml:id="UUID_494214c4-dfae-4abe-b081-984187aa7ee6">
                  <gml:exterior>
                    <gml:LinearRing gml:id="UUID_494214c4-dfae-4abe-b081-984187aa7ee6_0_">
                      <gml:posList srsDimension="3">466922.753 5895898.829 0.000 466920.691 5895897.909 0.000 466920.691 5895897.909 3.020 466922.753 5895898.829 3.020 466922.753 5895898.829 0.000</gml:posList>
                    </gml:LinearRing>
                  </gml:exterior>
                </gml:Polygon>
              </gml:surfaceMember>
              <gml:surfaceMember>
                <gml:Polygon gml:id="UUID_fd9a4e8e-2395-4cf9-9703-1e6f57bca116">
                  <gml:exterior>
                    <gml:LinearRing gml:id="UUID_fd9a4e8e-2395-4cf9-9703-1e6f57bca116_0_">
                      <gml:posList srsDimension="3">466923.833 5895896.411 0.000 466922.753 5895898.829 0.000 466922.753 5895898.829 3.020 466923.833 5895896.411 3.020 466923.833 5895896.411 0.000</gml:posList>
                    </gml:LinearRing>
                  </gml:exterior>
                </gml:Polygon>
              </gml:surfaceMember>
              <gml:surfaceMember>
                <gml:Polygon gml:id="UUID_95710ef6-38fd-4c49-870c-f41851c58c4a">
                  <gml:exterior>
                    <gml:LinearRing gml:id="UUID_95710ef6-38fd-4c49-870c-f41851c58c4a_0_">
                      <gml:posList srsDimension="3">466921.770 5895895.491 0.000 466923.833 5895896.411 0.000 466923.833 5895896.411 3.020 466921.770 5895895.491 3.020 466921.770 5895895.491 0.000</gml:posList>
                    </gml:LinearRing>
                  </gml:exterior>
                </gml:Polygon>
              </gml:surfaceMember>
              <gml:surfaceMember>
                <gml:Polygon gml:id="UUID_160af2ba-5a95-4acc-b0c3-c7505b23bfbf">
                  <gml:exterior>
                    <gml:LinearRing gml:id="UUID_160af2ba-5a95-4acc-b0c3-c7505b23bfbf_0_">
                      <gml:posList srsDimension="3">466921.770 5895895.491 0.000 466920.691 5895897.909 0.000 466922.753 5895898.829 0.000 466923.833 5895896.411 0.000 466921.770 5895895.491 0.000</gml:posList>
                    </gml:LinearRing>
                  </gml:exterior>
                </gml:Polygon>
              </gml:surfaceMember>
            </gml:CompositeSurface>
          </gml:exterior>
        </gml:Solid>
      </bldg:lod1Solid>
    </bldg:Building>
  </core:cityObjectMember>
  <core:cityObjectMember>
  [.......]

The files are stored in zip and currently processed with python and the packages zipfile, xml.etree and saved to the Database with geopandas appending to a specified table for each individual xml file.

I'm mostly struggling to properly save the geometries of each cityObjectMember in the xml files. Sometimes the height of the objects is not saved and sometimes (or both) overlapping geometries are saved.

The whole approach I'm using seems unnecessary complex for such a widely used dataset (Level-of-Detail 1 | LoD1 – Germany). Is there a better approach for this than processing these xml-Files with xml.etree similarly to what can be used with OpenStreetMap data (f.e. osm2pgsql)?

Best Answer

CityGML Data such as Level-of-Detail 1 can be uploaded with the 3D City Database Importer / Exporter Tool downloadable here: 3D City Database.

The setup is relatively straightforward and documented here.

I would still love to have a pure python solution tho as this is more than I need for what I want.