🏙️

PlateauのCityGMLをPythonのElementTreeでパースした時に出るns0とかいう邪魔者をぶっ潰す方法

2022/11/04に公開

ns0=Namespace

CityGMLをElementTreeでパースした時に現れるns0やns1が一体何者なのかというと、Namespace(名前空間)のことです。
そんな怖いものではありません。鬱陶しいですが。

これが

<?xml version="1.0" encoding="UTF-8"?>
<core:CityModel xmlns:brid="http://www.opengis.net/citygml/bridge/2.0" xmlns:wtr="http://www.opengis.net/citygml/waterbody/2.0" xmlns:tran="http://www.opengis.net/citygml/transportation/2.0" >

こうなる

<ns0:CityModel xmlns:ns0="http://www.opengis.net/citygml/2.0" xmlns:ns2="http://www.opengis.net/gml" xmlns:ns3="http://www.opengis.net/citygml/building/2.0" >

変換後に想定外の場所が変わっているとクソうざいですよね。今回はこのクソうざい現象を回避する方法を示していきます。

register_namespace

回避方法はシンプルで、register_namespace()で名前空間を設定してあげればいいだけ
こんな感じでね

from xml.etree import ElementTree as ET

tree = ET.parse('53394610_bldg_6697_2_op.gml')
# ここで名前空間を設定してあげる
ET.register_namespace('brid', "http://www.opengis.net/citygml/bridge/2.0")

ところがどっこいPlateauの名前空間は多すぎる

とりあえず丸々コピペ

<core:CityModel xmlns:brid="http://www.opengis.net/citygml/bridge/2.0" xmlns:wtr="http://www.opengis.net/citygml/waterbody/2.0" xmlns:tran="http://www.opengis.net/citygml/transportation/2.0" xmlns:gml="http://www.opengis.net/gml" xmlns:frn="http://www.opengis.net/citygml/cityfurniture/2.0" xmlns:veg="http://www.opengis.net/citygml/vegetation/2.0" xmlns:sch="http://www.ascc.net/xml/schematron" xmlns:tun="http://www.opengis.net/citygml/tunnel/2.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:tex="http://www.opengis.net/citygml/texturedsurface/2.0" xmlns:gen="http://www.opengis.net/citygml/generics/2.0" xmlns:dem="http://www.opengis.net/citygml/relief/2.0" xmlns:app="http://www.opengis.net/citygml/appearance/2.0" xmlns:luse="http://www.opengis.net/citygml/landuse/2.0" xmlns:xAL="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" xmlns:uro="http://www.kantei.go.jp/jp/singi/tiiki/toshisaisei/itoshisaisei/iur/uro/1.4" xmlns:bldg="http://www.opengis.net/citygml/building/2.0" xmlns:smil20="http://www.w3.org/2001/SMIL20/" xmlns:pbase="http://www.opengis.net/citygml/profiles/base/2.0" xmlns:smil20lang="http://www.w3.org/2001/SMIL20/Language" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:core="http://www.opengis.net/citygml/2.0" xmlns:grp="http://www.opengis.net/citygml/cityobjectgroup/2.0" xsi:schemaLocation="http://www.kantei.go.jp/jp/singi/tiiki/toshisaisei/itoshisaisei/iur/uro/1.4 http://www.kantei.go.jp/jp/singi/tiiki/toshisaisei/itoshisaisei/iur/schemas/uro/1.4/urbanObject.xsd http://www.opengis.net/citygml/2.0 http://schemas.opengis.net/citygml/2.0/cityGMLBase.xsd http://www.opengis.net/citygml/landuse/2.0 http://schemas.opengis.net/citygml/landuse/2.0/landUse.xsd http://www.opengis.net/citygml/building/2.0 http://schemas.opengis.net/citygml/building/2.0/building.xsd http://www.opengis.net/citygml/transportation/2.0 http://schemas.opengis.net/citygml/transportation/2.0/transportation.xsd http://www.opengis.net/citygml/generics/2.0 http://schemas.opengis.net/citygml/generics/2.0/generics.xsd http://www.opengis.net/citygml/cityobjectgroup/2.0 http://schemas.opengis.net/citygml/cityobjectgroup/2.0/cityObjectGroup.xsd http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/base/gml.xsd http://www.opengis.net/citygml/appearance/2.0 http://schemas.opengis.net/citygml/appearance/2.0/appearance.xsd">

Welcome to this crazy time
こんなんハードコーディングした日には面倒くさいわ名前空間の規格が変わったら使えないわコードが汚くなるわで私なら大爆発します。

ですので、プログラム的に読み込んで設定してあげましょう。

まぁ普通にattirib()してもxsiの部分だけで上記の名前空間の殆どが取れないんですけどね

{'{http://www.w3.org/2001/XMLSchema-instance}schemaLocation': 'http://www.kantei.go.jp/jp/singi/tiiki/toshisaisei/itoshisaisei/iur/uro/1.4 http://www.kantei.go.jp/jp/singi/tiiki/toshisaisei/itoshisaisei/iur/schemas/uro/1.4/urbanObject.xsd http://www.opengis.net/citygml/2.0 http://schemas.opengis.net/citygml/2.0/cityGMLBase.xsd http://www.opengis.net/citygml/landuse/2.0 http://schemas.opengis.net/citygml/landuse/2.0/landUse.xsd http://www.opengis.net/citygml/building/2.0 http://schemas.opengis.net/citygml/building/2.0/building.xsd http://www.opengis.net/citygml/transportation/2.0 http://schemas.opengis.net/citygml/transportation/2.0/transportation.xsd http://www.opengis.net/citygml/generics/2.0 http://schemas.opengis.net/citygml/generics/2.0/generics.xsd http://www.opengis.net/citygml/cityobjectgroup/2.0 http://schemas.opengis.net/citygml/cityobjectgroup/2.0/cityObjectGroup.xsd http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/base/gml.xsd http://www.opengis.net/citygml/appearance/2.0 http://schemas.opengis.net/citygml/appearance/2.0/appearance.xsd'}

さらにregister_namespace()の引数に入れる必要があるため、Key, Valueで名前空間を取得する必要があります。

ET.register_namespace('app', "http://www.opengis.net/citygml/appearance/2.0")

iterparse()を使う

xmlnsの名前空間をkey valueで取るにはiterparse()を使います。
iterparse()で取得したkey, valueのdictをforでregister_namespace()していきます。

namespaces = {node[0]: node[1] for _, node in ET.iterparse([ファイルパスorファイルオブジェクト], events=['start-ns'])}
# root = tree.getroot()
for key, value in namespaces.items(): 
  ET.register_namespace(key, value)

はい。これでちゃんと元通りの名前空間に対応したCityGMLになりました

<core:CityModel xmlns:app="http://www.opengis.net/citygml/appearance/2.0" xmlns:bldg="http://www.opengis.net/citygml/building/2.0" xmlns:core="http://www.opengis.net/citygml/2.0" >

コード全体

from xml.etree import ElementTree as ET

file = [ファイルパス]
tree = ET.parse(file)
namespaces = {node[0]: node[1] for _, node in ET.iterparse(file, events=['start-ns'])}
for key, value in namespaces.items(): 
  ET.register_namespace(key, value)

Discussion