CityGMLがQGISで正常に読めない件について
はじめに
CityGMLをQGISに食わせると、XYが逆に解釈されてしまう問題(とその解決)は記憶に新しいです。
これで建物データは簡単にQGISに取り込めるようになりましたが、他のデータで読み込めないものがまだたくさんあります(というより建物とDEM以外読めないはず)。この原因らしきものがGDALの実装にあったので、Issueで議論したらコアメンテナーの方が迅速に修正してくれたので、そのレポートです。
CityGMLをQGISで読むと…
たとえば東京都3D都市モデルに含まれるCityGMLデータで言うと、おそらく現状は建物とDEM以外は表示出来ないわけですが、表示できないパターンには2つあります。①謎の三角形が表示されるパターンと、②ジオメトリを認識出来ていないようなパターンです。
謎の三角形パターン
ジオメトリを認識出来ていないパターン
原因
謎の三角形の頂点座標を眺めるとなんとなく謎が解けるのですが、これらは3次元で格納された座標を2次元としてパースしていることが原因でした。
GMLの3次元座標の各要素は、y1 x1 z1 y2 x2 z2 ... yn xn znとスペース区切り文字列として格納されています。これを2次元として解釈すると、要素数が2で割り切れないか、たまたま要素数が偶数(=頂点数が偶数)の場合には誤って巨大な三角形として解釈していまうか、いずれかとなってしまいます(前者はジオメトリを認識できない②のパターン、後者は①謎の三角形パターンとなるわけです)。
GDALの実装の調査
コードを掘っていくと、下記のことがわかりました。
- GDALのデフォルトでは、GMLの座標を2次元として扱う
- この振る舞いは、
GML_SRS_DIMENSION_IF_MISSING
という環境変数をセットしておくことで変更できる - GMLの各ジオメトリに
srsDimension
というattributeがある場合、その次元として座標をパースする
CityGMLの調査
CityGMLは以下のような構造のXMLです(東京都3D都市モデルより)。
<?xml version="1.0" encoding="UTF-8"?>
<core:CityModel --省略-->
<gml:boundedBy>
<gml:Envelope srsName="http://www.opengis.net/def/crs/EPSG/0/6668" srsDimension="3">
<gml:lowerCorner>35.53992405142934 139.6726244934201 0</gml:lowerCorner>
<gml:upperCorner>35.58979203526172 139.753264136279 0</gml:upperCorner>
</gml:Envelope>
</gml:boundedBy>
<core:cityObjectMember>
<urf:DistrictsAndZones gml:id="urf_b678381f-3841-4691-8613-cba0b1ae3600">
<urf:class codeSpace="../../codelists/Common_districtsAndZonesType.xml">9</urf:class>
<urf:urbanPlanType codeSpace="../../codelists/Common_urbanPlanType.xml">21</urf:urbanPlanType>
<urf:prefecture codeSpace="../../codelists/Common_prefecture.xml">13</urf:prefecture>
<urf:city codeSpace="../../codelists/Common_localPublicAuthorities.xml">13100</urf:city>
<urf:lod0MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon>
<gml:exterior>
<gml:LinearRing>
<gml:posList>35.58294158132435 139.67425348499324 0 35.58293209070631 139.674236463604 0 ... 35.58294158132435 139.67425348499324 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</urf:lod0MultiSurface>
</urf:DistrictsAndZones>
</core:cityObjectMember>
<core:cityObjectMember>
以下略
envelopeにはsrsDimension
が定義されているものの、各ジオメトリのレベルには記載がないことがわかります。必ずしも無い訳ではなく、省略されることがある、というのが正しいです。東京都3D都市モデルで言うと、基本的には省略されているのですが、luse
では各ジオメトリに定義されています(だから読める)。
また、現状でもbldg
は読み込めますが、これはbldg
がSolid
なる地物タイプで定義されているためです。現状のGDALの実装では、Solid
の場合は座標を必ず3次元として扱うようになっていますコード。
Issueでの議論
当初、私はenvelopで定義されているsrsDimension
が、ジオメトリの解釈時に参照されれば解決するのではないか、と考えました(座標参照系を指定するsrsName
は、envelopの定義がグローバルに扱われているため、同様の扱いにするのが正しいのではないか、と)。
ここで、他のデベロッパーから、CityGMLの仕様書(?)の情報が共有され、CityGMLの座標は、基本的に3次元で格納されるということがわかりました。なので別のIssueを立てました。
こちらについて、Draftとして修正パッチを書いていましたが、この時点でコアメンテナーから反応があり「議論の筋は正しいよ、ワイが実装しちゃうネ」とコメントがあり、1日で実装・テスト追加・マージまでされました、さすがGDAL…。また、前者のIssueについても同様に実装・マージされました。なので、GDALの次のリリースではこれまで正常にパースできなかったCityGMLを読み込めるようになるはずです。
修正後のGDALで、CityGMLをSHPに変換、QGISで表示したもの、東京都3D都市モデル(tran)を利用
おわりに
年末年始に上記のパッチとテストコードを書いてGDALとC++のお勉強をしようと思っていたら一瞬で解決してしまったので少し残念ですが、こと現在の日本においては重要な修正に貢献出来たのかなと思っています。まだGDALが修正されただけなので、これがQGISにいつ取り込まれるかは定かではないですが、取り込まれればCityGMLの利用しやすさが一気に向上するものと思います。
Discussion