これなんの話?
BIMの標準データ交換フォーマットであるIFCのデータを可視化しようとした。
しかし、PythonのJupyter上で可視化しようとしたところ、IFCのデータを処理するためのライブラリ・IfcOpenShellに落とし穴があってドツボにはまったけど何とか解決したよっていうお話。
IFCのデータをPythonで可視化する
まずやらないといけないこと
IFCのデータのメッシュ化
IFCのデータは曲線やらなにやらのデータが入っていて、座標系は部屋や階ごとの相対座標系になっていたりする。
こいつを座標系展開して、形状をメッシュ化するのは大変。
ただ、IFCのデータを読み込んで、3Dメッシュ化する機能を持つライブラリは世の中に既に存在している。
それがIfcOpenshellである。他にもIFCEngineなんてものもあるが、こちらは研究目的なら無償だけどビジネスやらなにやらで使うならお金がかかる。
とりあえず、今回はpythonでコードを書くので使うのならばpythonのバインディングがあるIfcOpenshellでしょうと。
必要なもののインストール
ifcopenshellをインストールするにはcondaが必要だけど、Anaconda入れればOK
Anacondaでのcondaの使い方とか知らないっていう人は別途自分で調べてください()
pythonのバージョンは3.8でcondaの仮想環境は構築しました。
そのうえで、とりあえず以下のコマンドを実行すれば必要なものは入る…はず
conda install -c conda-forge -c oce -c dlr-sc -c ifcopenshell ifcopenshell
conda install -c conda-forge -c dlr-sc -c pythonocc -c oce pythonocc-core
pip install trimesh
いざコード作成
jupyterを起動してコードを書きました。
サンプルはIfcOpenHouseを使いました。
内容は以下の通り
import ifcopenshell
from ifcopenshell import geom
import trimesh
ifc_file = ifcopenshell.open("IfcOpenHouse_IFC2x3.ifc")
settings = geom.settings()
scene = trimesh.Scene()
for ifc_entity in ifc_file.by_type("IfcProduct"):
if ifc_entity.Representation:
shape = geom.create_shape(settings, ifc_entity)
ios_vertices = shape.geometry.verts
ios_faces = shape.geometry.faces
vertices = [[ios_vertices[i], ios_vertices[i+1], ios_vertices[i+2]] for i in range(0, len(ios_vertices), 3)]
faces = [[ios_faces[i], ios_faces[i+1], ios_faces[i+2]] for i in range(0, len(ios_faces), 3)]
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
scene.add_geometry(mesh)
scene.show()
これで可視化出来る…はずだった
ところが出てきたのはこんなやつだった
まあ、こうなってしまうのはIfcOpenshellの方のコードが色々とおかしいんでしょう…ってことで、色々調べてみた結果、どうやら座標系の設定の方に問題があることが分かった。
と、いうわけでその設定を行うようにしたコードだと以下の通りになる。
import ifcopenshell
from ifcopenshell import geom
import trimesh
# 3Dオブジェクトのシーングラフを作成
scene = trimesh.Scene()
def read_geom(ifc_path):
# ファイル読み込み
ifc_file = ifcopenshell.open(ifc_path)
settings = geom.settings()
# ワールド座標系を設定する(この設定がないとさっきみたいに変な三角形メッシュが出力される)
settings.set(settings.USE_WORLD_COORDS, True)
# IfcProductのサブクラスを全部取得する
for ifc_entity in ifc_file.by_type("IfcProduct"):
if ifc_entity.is_a("IfcOpeningElement"): continue
if ifc_entity.Representation:
# 設定に基づいてメッシュデータを作成
shape = geom.create_shape(settings, ifc_entity)
ios_vertices = shape.geometry.verts
ios_faces = shape.geometry.faces
# 出力した頂点リストと頂点インデックスのリストを整形
# どちらも要素3の配列が並んでる2次元配列に
vertices = [[ios_vertices[i], ios_vertices[i+1], ios_vertices[i+2]] for i in range(0, len(ios_vertices), 3)]
faces = [[ios_faces[i], ios_faces[i+1], ios_faces[i+2]] for i in range(0, len(ios_faces), 3)]
# trimeshを使って頂点インデックスと頂点リストからメッシュを生成
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
# メッシュをシーングラフに追加
scene.add_geometry(mesh)
read_geom("IfcOpenHouse_IFC2x3.ifc")
# シーングラフを可視化
scene.show()
これをjupyter上で実行して得られた画像がこちら